﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NAudio.Wave;
using System.Net;
using System.IO;
using log4net;

namespace NRadioTray.App.Player
{
    public class SimplePlayer : IPlayer
    {
        private Stream ms = new MemoryStream();
        private Thread bufferThread;
        private Thread playerThread;
        private IPlayerObserver viewObserver;
        private ILog logger;

        private object lockFlag = new object();
        private PlaybackState stateToSet;
        private PlaybackState currentState;

        public SimplePlayer(IPlayerObserver observer, ILog logger)
        {
            this.viewObserver = observer;
            this.logger = logger;
            
            this.viewObserver.SetStatus(PlayerState.Stopped, string.Empty);

            lock (lockFlag)
            {
                this.stateToSet = PlaybackState.Playing;
            }
        }

        public void Play(string url)
        {
            this.playerThread = new Thread(delegate(object o)
            {
                this.bufferThread = new Thread(delegate(object z)
                {
                    try
                    {
                        var request = WebRequest.Create(url);
                        request.Timeout = 3000;
                        var response = request.GetResponse();
                        using (var stream = response.GetResponseStream())
                        {
                            byte[] buffer = new byte[65536]; // 64KB chunks
                            int read;
                            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                var pos = ms.Position;
                                ms.Position = ms.Length;
                                ms.Write(buffer, 0, read);
                                ms.Position = pos;
                            }
                        }
                    }
                    catch(Exception exception)
                    {
                        lock (this.lockFlag)
                        {
                            this.stateToSet = PlaybackState.Stopped;
                        }

                        this.logger.Error("Error in buffer", exception);
                    }
                }) { IsBackground = true , Name = "BufferThread"};
                this.bufferThread.Start();

                // Pre-buffering some data to allow NAudio to start playing
                this.viewObserver.SetStatus(PlayerState.Buffering, string.Empty);
                while (ms.Length < 65536 * 10)
                {
                    Thread.Sleep(1000);

                    lock (this.lockFlag)
                    {
                        if (this.stateToSet == PlaybackState.Stopped)
                        {
                            this.viewObserver.SetStatus(PlayerState.Error, "Timeout");
                            return;
                        }
                    }
                }

                ms.Position = 0;

                this.viewObserver.SetStatus(PlayerState.Playing, string.Empty);

                using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
                {
                    using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                    {
                        waveOut.Init(blockAlignedStream);
                        waveOut.Play();
                        while (waveOut.PlaybackState == PlaybackState.Playing)
                        {
                            System.Threading.Thread.Sleep(100);

                            lock (this.lockFlag)
                            {
                                if (this.stateToSet == PlaybackState.Stopped)
                                {
                                    waveOut.Stop();
                                }

                                this.currentState = waveOut.PlaybackState;
                            }
                        }

                        this.viewObserver.SetStatus(PlayerState.Stopped, "Koniec odtwarzania");
                    }
                }

                this.viewObserver.SetStatus(PlayerState.Stopped, string.Empty);
            }) { IsBackground = true , Name = "PlayerThread"};
            
            this.playerThread.Start();

        }

        public void Pause()
        {
            throw new NotImplementedException();
        }

        public void Stop()
        {
            lock (lockFlag)
            {
                this.stateToSet = PlaybackState.Stopped;
            }

            while (this.currentState != PlaybackState.Stopped)
            {
                System.Threading.Thread.Sleep(100);
            }

            if (this.bufferThread != null)
            {
                this.bufferThread.Abort();
                this.bufferThread = null;
            }

            if (this.playerThread != null)
            {
                this.playerThread.Abort();
                this.playerThread = null;
            }  
        }

        public void Dispose()
        {
            this.Stop();
            this.viewObserver = null;            
        }
    }
}
